#include "../pch/pch.h"

using namespace e::sys;

class ThreadPool : public IThreadPool {

	PTR_DEF(Worker);

	class Worker :
		public IRunnable,
		public boost::enable_shared_from_this<Worker> {
	private:
		IThreadPtr _thread;
		e::sys::Mutex _mutex;
		SyncPoint _syncPoint;
		volatile bool _running;
		IRunnablePtr _runnable;

	public:
		Worker():_running(true) {
			E_DBG2("Construct()");
		}

		~Worker() {
			E_DBG2("Destruct()");
			if (_thread) {
				_thread->join();
			}
		}

	public:
		void start() {
			_thread = IThread::New(shared_from_this());
		}

		bool isFree() {
			ScopedLock lock(_mutex);
			return _runnable ? false : true;
		}

		bool addRunnable(const IRunnablePtr& runnable) {
			if (!runnable || !_running) {
				return false;
			}
			{
				ScopedLock lock(_mutex);
				if (_runnable) {
					return false;
				}
				_runnable = runnable;
			}
			_syncPoint.signal();
			return true;
		}

	private:
		//////////////////////////////////////////////////////////////////////////
		// IRunnable
		void run() {
			while (_running) {
				if (isFree()) {
					_syncPoint.wait(1000);
					continue;
				}
				_runnable->run();
				ScopedLock lock(_mutex);
				_runnable.reset();
			}
		}

		void onJoin() {
			_running = false;
			{
				IRunnablePtr runnable;
				{
					ScopedLock lock(_mutex);
					runnable = _runnable;
				}
				if (runnable) {
					runnable->onJoin();
				}
			}
			_syncPoint.signal();
		}
	};

private:
	e::sys::Mutex _mutex;
	typedef std::vector<WorkerPtr> WorkerArray;
	WorkerArray _workers;

public:
	ThreadPool() {
		E_DBG2("Construct()");
	}

	~ThreadPool() {
		E_DBG2("Destruct()");
	}

private:
	//////////////////////////////////////////////////////////////////////////
	// IThreadPool
	void submit(const IRunnablePtr& runnable) {
		WorkerPtr freeWorker;
		ScopedLock lock(_mutex);
		for (UINT32 i = 0; i < _workers.size(); i ++) {
			WorkerPtr& worker = _workers[i];
			if (worker->isFree()) {
				freeWorker = worker;
				break;
			}
		}
		if (!freeWorker) {
			freeWorker = WorkerPtr(new Worker);
			freeWorker->start();
			_workers.push_back(freeWorker);
		}
		freeWorker->addRunnable(runnable);
	}
};


IThreadPoolPtr IThreadPool::New() {
	return IThreadPoolPtr(new ThreadPool);
}